package com.example.zyl.common.enums;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.example.zyl.api.entity.OperateLog;
import com.example.zyl.common.model.SaveBatchList;
import com.example.zyl.common.utils.RedisUtils;
import com.example.zyl.common.utils.constant.RedisConstant;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
@Getter
@AllArgsConstructor
public enum RedisQueueEnum {

    OPERATE_LOG("operate_log", "操作日志队列", 1000, TimeUnit.MILLISECONDS) {
        @Override
        public void execute(Object obj) {
            if (obj == null) {
                return;
            }
            if (obj instanceof OperateLog) {
                OperateLog operateLog = BeanUtil.toBean(obj, OperateLog.class);
                operateLog.insert();
                // 保存到redis
                String operateLogKey = RedisConstant.REDIS_KEY_PREFIX.concat(RedisConstant.OPERATE_LOG);
                RedisUtils redisUtils = SpringUtil.getBean(RedisUtils.class);
                redisUtils.hset(operateLogKey, String.valueOf(operateLog.getOperateId()), operateLog);
            }
        }
    },

    BACH_SAVE("bach_save", "批量存入队列", 1000, TimeUnit.MILLISECONDS) {
        @Override
        public void execute(Object obj) {

            if (obj == null) return;

            if (!(obj instanceof SaveBatchList)) return;

            // 转换类型
            SaveBatchList<?> bachSaveList = (SaveBatchList<?>) obj;

            // 获取数据
            List<?> records = bachSaveList.getBachList();
            // 获取mapper
            Class<?> bachMapperClass = bachSaveList.getBachMapperClass();
            // 获取分片大小
            int batchSize = bachSaveList.getBachSize();
            // 校验数据
            if (records == null || records.isEmpty() ||
                    batchSize <= 0 || bachMapperClass == null) return;

            // 获取方法名
            String bachMethodName = bachSaveList.getBachMethodName();

            // 根据分片大小调整
            CountDownLatch latch = new CountDownLatch((records.size() + batchSize - 1) / batchSize);
            // 数据分片
            List<? extends List<?>> recordsList = Lists.partition(records, batchSize);

            // 获取sqlSession
            SqlSessionFactory factory = SpringUtil.getBean(SqlSessionFactory.class);
            //获取线程池
            ThreadPoolTaskExecutor taskExecutor = SpringUtil.getBean(this.getThreadName(), ThreadPoolTaskExecutor.class);

            // 遍历分片
            recordsList.forEach(record -> {
                // 异步执行
                taskExecutor.submit(() -> {
                    // 开启一个新的sqlSession
                    SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);
                    try {
                        Object mapper = sqlSession.getMapper(bachMapperClass);
                        mapper.getClass().getMethod(bachMethodName, List.class).invoke(mapper, record);
                        // 提交当前会话
                        sqlSession.commit();
                    } catch (Exception e) {
                        log.error("批量插入数据失败 ===> [{}]", e.getMessage(), e);
                        // 回滚事务
                        sqlSession.rollback();
                    } finally {
                        // 计数器减少
                        latch.countDown();
                        // 清除缓存
                        sqlSession.clearCache();
                        // 确保会话关闭
                        sqlSession.close();
                    }
                });
            });

            try {
                // 等待所有的任务完成
                latch.await();
            } catch (InterruptedException e) {
                // 中断当前线程，恢复中断状态
                Thread.currentThread().interrupt();
                log.error("线程等待异常 ===> [{}]", e.getMessage(), e);
            }
        }
    };


    /**
     * 队列名称
     */
    private final String queueName;

    /**
     * 队列描述
     */
    private final String description;

    /**
     * 超时时间
     */
    private final long delay;

    /**
     * 超时时间单位
     */
    private final TimeUnit timeUnit;


    public abstract void execute(Object obj);


    public void startLog(StopWatch stopWatch) {
        stopWatch.start();
        log.info("执行[{}]任务开始", this.description);
    }


    public void endLog(StopWatch stopWatch) {
        stopWatch.stop();
        long time = stopWatch.getTime();
        log.info("执行[{}]任务结束，耗时：[{}] ms", this.description, time);
    }


    public String getThreadName() {
        return "threadAsyncExecutor";
    }
}
